﻿using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;

namespace HB.Base
{
    /// <summary>
    /// 限量并行队列任务管理器
    /// </summary>
    public class QueueTasks<T>
    {
        /// <summary>
        /// 完成某个任务
        /// </summary>
        public event Action<T> OnCompletedOne;
        /// <summary>
        /// 任务失败
        /// </summary>
        public event Action<T, Exception> OnFail;
        /// <summary>
        /// first int=finishSum,second int=taskSum
        /// </summary>
        public event Action<int, int> OnProcessChanged;
        ConcurrentQueue<T> QueueTask;
        /// <summary>
        /// 最大并行数量，后期可设置
        /// </summary>
        public int MaxChannal { get; set; }
        Action<T> TaskAction { get; set; }
        ConcurrentDictionary<Guid, int> DicChannal = new ConcurrentDictionary<Guid, int>();
        public int LeftTaskCount => QueueTask.Count;
        public int LeftDoingTaskCount => DicChannal.Count;
        public bool IsBusy => DicChannal.Count > 0;
        int FinishSum { get; set; }
        int TaskSum { get; set; }
        public double Progress => FinishSum * 1.0 / TaskSum;
        /// <summary>
        /// 是否正在进行任务处理
        /// </summary>
        public bool IsActive { get; private set; }
        /// <summary>
        /// 清理所有任务
        /// </summary>
        public void Clear()
        {
            IsActive = false;
            FinishSum = 0;
            TaskSum = 0;
            while (QueueTask.TryDequeue(out var t)) { }
            OnProcessChanged?.Invoke(0, 0);
            DicChannal.Clear();
        }
        /// <summary>
        /// 启用处理
        /// </summary>
        public void Start()
        {
            IsActive = true;
            Active(QueueTask.Count);
        }
        /// <summary>
        /// 暂停处理
        /// </summary>
        public void Pause()
        {
            IsActive = false;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="maxChannal">最大并行数量</param>
        /// <param name="Action">将在后台task中运行的委托</param>
        public QueueTasks(int maxChannal, Action<T> Action)
        {
            this.MaxChannal = maxChannal;
            this.TaskAction = Action;
            DicChannal = new ConcurrentDictionary<Guid, int>();
            QueueTask = new ConcurrentQueue<T>();
        }
        /// <summary>
        /// 增加一个任务
        /// </summary>
        /// <param name="task"></param>
        /// <param name="autoStart">自动开启</param>
        public void AddTask(T task, bool autoStart = true)
        {
            if (autoStart) { IsActive = true; }
            TaskSum++;
            QueueTask.Enqueue(task);
            OnProcessChanged?.Invoke(FinishSum, TaskSum);
            if (autoStart)
            {
                Active(QueueTask.Count);
            }
        }
        public void AddTaskAsync(T task, bool autoStart = true)
        {
            if (autoStart) { IsActive = true; }
            TaskSum++;
            Task.Factory.StartNew(() =>
            {
                QueueTask.Enqueue(task);
                OnProcessChanged?.Invoke(FinishSum, TaskSum);
                if (autoStart)
                {
                    Active(QueueTask.Count);
                }
            });


        }
        /// <summary>
        /// 激活并行任务
        /// </summary>
        /// <param name="currentTaskCount">此时的任务量，自动匹配可分配的计算通道</param>
        void Active(int currentTaskCount)
        {
            if (!IsActive) { return; }
            if (currentTaskCount == 0) { return; }
            int channalNeed = Math.Min(MaxChannal, currentTaskCount);
            int leftChannal = MaxChannal - DicChannal.Count;

            int newChannal = Math.Min(channalNeed, leftChannal);
            for (int i = 0; i < newChannal; i++)
            {
                var id = Guid.NewGuid();
                DicChannal[id] = 0;

                var th = Task.Factory.StartNew(() =>
                {
                    try
                    {
                        while (QueueTask.TryDequeue(out var task))
                        {
                            try
                            {
                                TaskAction.Invoke(task);
                                OnCompletedOne?.Invoke(task);
                                lock (QueueTask)
                                {
                                    FinishSum++;
                                }
                            }
                            catch (Exception ex)
                            {
                                OnFail?.Invoke(task, ex);
                            }



                            OnProcessChanged?.Invoke(FinishSum, TaskSum);
                            if (!IsActive) { break; }
                        }

                    }

                    finally
                    {
                        DicChannal.TryRemove(id, out int j);
                    }


                });

            }
            if (DicChannal.Count == 0)
            {
                Active(QueueTask.Count);
            }
        }
    }
}
